package server

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"strconv"
	"sync"
	"synthesis-server/model"
)

var MyServer = NewServer()

type Server struct {
	Clients       map[string]*Client //当前在线的客户和客服
	Send          chan []byte
	ServerIn      chan *Client
	IdleServer    *LockedQueue        //空闲客服队列
	WaitingClient *LockedQueue        //客户端排队队列
	OnlineServer  int                 //在线客服的数量
	Mutex         sync.Mutex          //用于在操作在线客服数时加锁
	Calling       map[*Client]*Client //用于存储正在通话的客服客户对
	CancelLine    chan *Client        //客户取消排队
}

func NewServer() *Server {
	return &Server{
		Clients:       make(map[string]*Client),
		Send:          make(chan []byte),
		ServerIn:      make(chan *Client),
		IdleServer:    NewQueue(),
		WaitingClient: NewQueue(),
		Mutex:         sync.Mutex{},
		Calling:       make(map[*Client]*Client),
		OnlineServer:  0,
		CancelLine:    make(chan *Client),
	}
}

func (s *Server) Start() {
	for {
		select {

		//服务器直接转发消息的情况
		case message := <-s.Send:

			msg := &model.Message{}

			e := json.Unmarshal([]byte(string(message)), &msg)

			if e != nil {
				fmt.Print("json解析错误:")
				fmt.Println(e)
			}

			client, ok := s.Clients[msg.To]

			if ok {
				client.Send <- message
			} else {
				//接收方不在线,返回发送失败消息给发送方
				d := model.Datas{DataContent: "对方不在线,您的信息发送失败!", DataType: "err"}
				m := &model.Msg{Data: d}
				data, err := json.Marshal(m)
				if err != nil {
					fmt.Println("服务器创建的msg对象转为json时出现了问题")
				}

				sender, t := s.Clients[msg.From]
				if t {
					sender.Conn.WriteMessage(websocket.TextMessage, data)
				}
			}

		// 当有客服登录服务器或者有客服结束通话时 此时需要检查当前是否有人在排队,若有人排队则与队首的人建立通话,并向其发送连接成功建立的消息
		case s := <-s.ServerIn:

			if len(MyServer.WaitingClient.Data) != 0 {
				sender := MyServer.WaitingClient.Pop()

				sender.IsLining = false

				//向客户发送连接成功建立的消息
				MyServer.Calling[sender] = s
				d := model.Datas{DataType: "p2pConnect", DataContent: s.Name}
				m := model.Msg{Data: d}
				data, err := json.Marshal(m)
				if err != nil {
					fmt.Println("服务器向客户发送连接建立成功的消息json序列化不成功!")
				}
				sender.Conn.WriteMessage(websocket.TextMessage, data)

				//向客服发送连接成功建立的消息
				da := model.Datas{DataType: "p2pConnect", DataContent: sender.Name}
				ma := model.Msg{Data: da}
				dataa, err := json.Marshal(ma)
				if err != nil {
					fmt.Println("服务器向客户发送连接建立成功的消息json序列化不成功!")
				}
				s.Conn.WriteMessage(websocket.TextMessage, dataa)

				MyServer.broadcast()
			} else {
				MyServer.IdleServer.Add(s)
			}
			MyServer.BroadcastStatus()

		//客户取消了排队
		case c := <-s.CancelLine:
			n := len(s.WaitingClient.Data)
			temp := NewQueue()
			broadca := false
			for i := 0; i < n; i++ {
				t := s.WaitingClient.Pop()

				if broadca {
					d := model.Datas{DataType: "FrontPeople", DataContent: strconv.Itoa(i - 1)}
					m := model.Msg{Data: d}
					data, err := json.Marshal(m)
					if err != nil {
						fmt.Println("服务器向客户发送连接建立成功的消息json序列化不成功!")
					}
					t.Conn.WriteMessage(websocket.TextMessage, data)
				}
				if t.Name == c.Name {
					c.IsLining = false
					delete(s.Clients, c.Name)
					c.Conn.Close()
					broadca = true
					continue
				}

				temp.Add(t)
			}

			for i := 0; i < n-1; i++ {
				s.WaitingClient.Add(temp.Pop())
			}

			MyServer.BroadcastStatus()

		}
	}
}

//向当前正在排队的所有用户广播这些用户前面的人数
func (s *Server) broadcast() {
	num := len(s.WaitingClient.Data)
	temp := NewQueue()
	for i := 0; i < num; i++ {
		c := s.WaitingClient.Pop()
		d := model.Datas{DataType: "FrontPeople", DataContent: strconv.Itoa(i)}
		m := model.Msg{Data: d}
		data, err := json.Marshal(m)
		if err != nil {
			fmt.Println("服务器向客户发送连接建立成功的消息json序列化不成功!")
		}
		c.Conn.WriteMessage(websocket.TextMessage, data)
		temp.Add(c)
	}
	for i := 0; i < num; i++ {
		s.WaitingClient.Add(temp.Pop())
	}
}

/*
	带有互斥锁的队列,队列中的元素为client类型的指针
*/

func NewQueue() *LockedQueue {
	return &LockedQueue{
		mutex: sync.Mutex{},
	}
}

type LockedQueue struct {
	Data  []*Client
	mutex sync.Mutex
}

func (q *LockedQueue) Add(k *Client) {
	q.mutex.Lock()
	q.Data = append(q.Data, k)
	q.mutex.Unlock()
}

func (q *LockedQueue) Pop() *Client {
	q.mutex.Lock()
	if len(q.Data) == 0 {
		q.mutex.Unlock()
		return nil
	}
	v := q.Data[0]
	q.Data = q.Data[1:]
	q.mutex.Unlock()
	return v
}

/*
	向当前在线的客服广播排队人数和在线客服数量的函数
*/

func (s *Server) BroadcastStatus() {

	//装载发送信息
	status := model.Status{OnlineServer: strconv.Itoa(MyServer.OnlineServer), WaitingClients: strconv.Itoa(len(MyServer.WaitingClient.Data))}
	//msg, er := json.Marshal(status)
	//if er != nil {
	//	fmt.Println("广播发送status信息失败")
	//}
	dd := model.Datas{DataType: "status", DataContent: status}
	send := model.Msg{Data: dd}
	sendData, e := json.Marshal(send)
	if e != nil {
		fmt.Println("广播发送status信息失败")
	}

	//	找出在线客服并发送信息
	for _, k := range MyServer.Clients {
		if k.IsServer == true {
			MyServer.Mutex.Lock()
			k.Conn.WriteMessage(websocket.TextMessage, sendData)
			MyServer.Mutex.Unlock()
		}
	}
}

/*
	当客服全部不在线时调用此方法
*/
func (s *Server) ServerAllDisconnect() {
	n := len(MyServer.WaitingClient.Data)
	for i := 0; i < n; i++ {
		c := MyServer.WaitingClient.Pop()

		d := model.Datas{DataType: "disconnected", DataContent: "所有客服已掉线"}
		m := model.Msg{Data: d}
		data, err := json.Marshal(m)
		if err != nil {
			fmt.Println("服务器向客户发送当前没有客服在线!此会话关闭的消息json序列化不成功!")
		}
		or := c.Conn.WriteMessage(websocket.TextMessage, data)
		if or != nil {
			fmt.Println("用户掉线,写方法错误")
		}
		delete(MyServer.Clients, c.Name)
		p := c.Conn.Close()
		if p != nil {
			println("关闭")
		}
	}
}
